/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2014 All Rights Reserved.
 */
package com.david.common.rpc.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.david.common.rpc.RpcService;
import com.david.common.rpc.Handler.RpcHandler;
import com.david.common.rpc.Response.RpcResponse;
import com.david.common.rpc.registry.ServiceRegistry;
import com.david.common.rpc.request.RpcRequest;
import com.david.common.util.LogUtils;

/**
 *Rpc 服务端
 *该类实现了ApplicationContextAware, 这样在ApplicationContext运行的时候会通知到该Bean
 * @author zhangwei_david
 * @version $Id: SrServer.java, v 0.1 2014年12月31日 下午8:46:33 zhangwei_david Exp $
 */
public class RpcServer implements ApplicationContextAware, InitializingBean {

    private static final Logger logger     = LogManager.getLogger(RpcServer.class);

    private String              serverAddress;

    private ServiceRegistry     serviceRegistry;

    private Map<String, Object> handlerMap = new HashMap<String, Object>();

    /**
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline().addLast(new RpcDecoder(RpcRequest.class)) // 将 RPC 请求进行解码（为了处理请求）
                            .addLast(new RpcEncoder(RpcResponse.class)) // 将 RPC 响应进行编码（为了返回响应）
                            .addLast(new RpcHandler(handlerMap)); // 处理 RPC 请求
                    }
                }).option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);

            String[] array = serverAddress.split(":");
            String host = array[0];
            int port = Integer.parseInt(array[1]);

            ChannelFuture future = bootstrap.bind(host, port).sync();

            if (serviceRegistry != null) {
                serviceRegistry.register(serverAddress); // 注册服务地址
            }

            future.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    /**
     * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
     */
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {

        Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class); // 获取所有带有 RpcService 注解的 Spring Bean
        LogUtils.info(logger, "获取到所有的RPC服务有：{0}", serviceBeanMap);
        if (MapUtils.isNotEmpty(serviceBeanMap)) {
            for (Object serviceBean : serviceBeanMap.values()) {
                String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).inf()
                    .getName();
                handlerMap.put(interfaceName, serviceBean);
            }
        }
    }

    /**
     * Getter method for property <tt>serverAddress</tt>.
     *
     * @return property value of serverAddress
     */
    public String getServerAddress() {
        return serverAddress;
    }

    /**
     * Setter method for property <tt>serverAddress</tt>.
     *
     * @param serverAddress value to be assigned to property serverAddress
     */
    public void setServerAddress(String serverAddress) {
        this.serverAddress = serverAddress;
    }

    /**
     * Getter method for property <tt>serviceRegistry</tt>.
     *
     * @return property value of serviceRegistry
     */
    public ServiceRegistry getServiceRegistry() {
        return serviceRegistry;
    }

    /**
     * Setter method for property <tt>serviceRegistry</tt>.
     *
     * @param serviceRegistry value to be assigned to property serviceRegistry
     */
    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

}
